<style>
.customTable {
  padding: 0 12px 12px 12px;
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
  background: white;
}
.row1 {
  display: flex;
  min-height: 74px;
  align-items: center;
}
.condition {
  width: calc(100% - 150px);
  height: 100%;
  display: flex;
  flex-wrap: wrap;
  overflow-y: auto;
  align-items: center;
}
.condition > div {
  height: 34px;
  display: flex;
  align-items: center;
  margin-right: 24px;
}
.condition > div > div,
.condition > div > span,
.condition > div > input {
  width: 150px;
  text-align: left;
}

.row2 {
  height: 64px;
  width: 100%;
  border-top: solid 1px #e9e9e9;
  display: flex;
  align-items: flex-end;
  justify-content: flex-start;
  padding-bottom: 10px;
}
.row2 > button {
  margin-right: 10px;
}

.ant-pagination {
  position: absolute;
  bottom: 12px;
  right: 12px;
}
</style>
<template>
  <div
    id="customTable"
    class="customTable">
    <div
      v-show="showConditionSlot"
      id="condition"
      class="row1">
      <div
        class="condition"
        style="marginRight:10px;">
        <slot name="condition" />
      </div>
      <a-button
        style="marginRight:10px;"
        @click="queryFromBtn">
        <template #icon> <SearchOutlined /> </template>查询
      </a-button>
      <a-button
        style="width:48px;"
        @click="reset">
        <template #icon>
          <RedoOutlined />
        </template>
      </a-button>
    </div>
    <div
      v-show="showOperateSlot"
      id="operate"
      class="row2">
      <slot name="operate" />
    </div>
    <a-table
      ref="refTable"
      :row-selection="showSelection ? rowSelection : null"
      :columns="table.columns"
      size="small"
      :row-key="rowKey"
      :data-source="table.data"
      :pagination="false"
      class="table"
      :scroll="{ y: tableContentHeight, x: '100%' }"
      :loading="table.loading"
    >
      <template
        v-for="(name, index) in table.customColumns"
        :key="index"
        #[name]="{record}">
        <slot
          :name="name"
          :record="record"/>
      </template>
      <template
        v-if="buttonList.list"
        #_operate="{record}">
        <a-button
          v-for="(btn, index) in buttonList.list"
          :key="index"
          type="link"
          :danger="btn.btnType == 'danger'"
          @click="onBtnClick(record, btn)"
        >{{ btn.label }}</a-button>
      </template>
    </a-table>
    <a-pagination
      size="small"
      :current="page.pageNum"
      :page-size="page.pageSize"
      :page-size-options="pageSizeList"
      :total="page.total"
      show-size-changer
      show-quick-jumper
      :show-total="total => `共${total}条`"
      @change="pageChage"
      @showSizeChange="sizeChange"
    />
    <deleteConfirm
      v-model:visible="tipVisible"
      @onOk="deleteRowAfterConfirm" />
  </div></template>
<script setup>
import { ref, defineProps, reactive, watch, defineExpose, defineEmits, onMounted } from 'vue'
import { SearchOutlined, RedoOutlined } from '@ant-design/icons-vue'
import { message } from 'ant-design-vue'
import Sortable from 'sortablejs'
import { service as request } from '@/api/request'

const props = defineProps({
  // 分页查询url
  queryUrl: {
    type: String,
    required: true
  },
  // 批量删除url
  deleteUrl: {
    type: String,
    required: true
  },
  // 表头
  header: {
    type: Array,
    required: true
  },
  // 表格是否显示序号列
  showIndex: {
    type: Boolean,
    default: true
  },
  // 是否禁用排序
  sortDisabled: {
    type: Boolean,
    default: true
  },
  // 是否可进行排序
  isSort: {
    type: Boolean,
    default: true
  },
  // 序号列配置
  indexColumn: {
    type: Object,
    default: undefined
  },
  // 表格是否显示勾选列
  showSelection: {
    type: Boolean,
    default: true
  },
  // 表格操作栏按钮
  buttonList: {
    type: Object,
    default: () => ({
      width: 160,
      title: '操作',
      fixed: 'right',
      list: [
        { btnType: 'primary', label: '编辑', eventType: 'edit' },
        { btnType: 'danger', label: '删除', eventType: 'delete' }
      ]
    })
  },
  // 数据
  rowKey: {
    type: String,
    default: 'id'
  },
  // 数据过滤器 请求成功后 会调用此属性方法过滤数据
  dataFilter: {
    type: Function,
    default: undefined
  },
  // 支持的分页条目数
  pageSizeList: {
    type: Array,
    default: () => ['10', '20', '50', '100']
  },
  align: {
    type: String,
    default: 'left'
  },
  showConditionSlot: {
    type: Boolean,
    default: true
  },
  showOperateSlot: {
    type: Boolean,
    default: true
  },
  overwriteDeleteRequest: {
    type: Function,
    default: undefined
  }
})

// 排序信息
const sortable = ref()
const refTable = ref()
// 分页信息
const page = reactive({
  pageSize: 10,
  pageNum: 1,
  total: 0
})
/** 获取表格高度，用于表格高度自适应 */
const tableContentHeight = ref(0)
const tipVisible = ref(false)
// 表格 selection
const rowSelection = reactive({
  selectedRowKeys: [],
  selectedRows: [],
  onChange: (selectedRowKeys, selectedRows) => {
    rowSelection.selectedRowKeys = selectedRowKeys
    rowSelection.selectedRows = selectedRows
    emit('selectedRowChange', { selectedRowKeys, selectedRows })
  }
})
// 表格信息
const table = reactive({
  columns: [],
  data: [],
  customColumns: [],
  loading: false
})
let conditionCache
// 对外事件
const emit = defineEmits(['query', 'reset', 'operate', 'selectedRowChange', 'pageChange', 'deleted'])
// 页码变化
const pageChage = (pageNum, pageSize) => {
  page.pageNum = pageNum
  emit('pageChange', { pageNum, pageSize })
  query()
}

// 每页条目数变化
const sizeChange = (pageNum, pageSize) => {
  page.pageNum = 1
  page.pageSize = pageSize
  emit('pageChange', { pageNum: 1, pageSize })
  query()
}

// 查询按钮
const queryFromBtn = () => {
  emit('query')
}
/**
 * 重置并查询
 */
const reset = () => {
  emit('reset')
  resetQuery()
}

/**
 * 初始化表格拖动
 */
const initSortable = () => {
  const mytb = refTable.value.$el.querySelector('tbody')
  sortable.value = new Sortable(mytb, {
    handle: '.ant-table-row',
    animation: 150,
    ghostClass: 'blue-background-class',
    sort: true,
    // disabled: props.sortDisabled,
    onMove () {
      if (props.isSort) {
        return true
      }
      return false
    },
    async onUpdate ({ newIndex, oldIndex }) {
      emit('sortTable', { newIndex, oldIndex, data: table.data })
    }
  })
}

/**
 * 分页查询
 */
const query = () => {
  table.loading = true
  const url = props.queryUrl
  const method = 'POST'
  let { pageSize, pageNum } = page
  const data1 = {
    condition: conditionCache,
    pageSize,
    pageNum
  }
  request({ url, method, data: data1 })
    .then(({ data, resCode, resMsg }) => {
      if (resCode === '00000') {
        let { total, rows } = data
        page.total = total
        if (props.dataFilter) rows = props.dataFilter(rows || [])
        rows = rows || []
        if (props.showIndex) {
          const key = props.indexColumn && props.indexColumn.dataIndex ? props.indexColumn.dataIndex : 'index'
          const result = []
          for (let i = 0; i < rows.length; i++) {
            let item = rows[i]
            item[key] = (pageNum - 1) * pageSize + i + 1
            result.push(item)
          }
          rows = result
        }
        table.data = rows
      } else {
        message.error(resMsg)
      }
      table.loading = false
    })
    .catch(() => {
      table.loading = false
    })
}
/**
 * 条件查询
 */
const queryByCondition = (condition) => {
  conditionCache = condition instanceof Object ? JSON.parse(JSON.stringify(condition)) : undefined
  resetPage()
  query()
}
/**
 * 重置并查询
 */
const resetQuery = () => {
  conditionCache = undefined
  resetPage()
  query()
}
/**
 * 重置page属性
 */
const resetPage = () => {
  page.pageSize = Number(props.pageSizeList[0])
  page.pageNum = 1
}
/**
 * 批量删除
 * @param  {String} ids 数据id集合，中间用',' 分割
 */
const deleteRows = (ids) => {
  let requestBody
  if (props.overwriteDeleteRequest && typeof props.overwriteDeleteRequest === 'function') {
    requestBody = props.overwriteDeleteRequest(ids)
  } else {
    requestBody = request({
      url: props.deleteUrl + ids,
      method: 'delete'
    })
  }
  requestBody.then(({ data, resCode, resMsg }) => {
    if (resCode === '00000') {
      message.success('删除成功!')
      let idsArr = ids.split(',')
      let deleteNum = idsArr.length
      let { total, pageNum, pageSize } = page
      total -= deleteNum
      if (pageNum !== 1 && (total <= pageSize * (pageNum - 1))) page.pageNum = pageNum - 1
      query()
      let { selectedRowKeys, selectedRows } = rowSelection
      rowSelection.selectedRowKeys = selectedRowKeys.filter((key) => !idsArr.includes(`${key}`))
      rowSelection.selectedRows = selectedRows.filter((row) => !idsArr.includes(`${row[props.rowKey]}`))
      emit('deleted', ids)
    } else {
      message.error(resMsg)
    }
  })
}
/**
 * 删除数据提示窗 确定按钮事件
 */
let deletingRowIds
const deleteRowAfterConfirm = () => {
  tipVisible.value = false
  if (deletingRowIds)deleteRows(`${deletingRowIds}`)
}

/**
 * 对外提供的删除方法
 */
const deleteRow = (rowKey) => {
  deletingRowIds = rowKey
  tipVisible.value = true
}
/**
 * 表格操作栏按钮事件
 */
const onBtnClick = (row, btn) => {
  const { eventType } = btn
  emit('operate', { eventType, data: row })
  if (eventType === 'delete') {
    deletingRowIds = `${row[props.rowKey]}`
    tipVisible.value = true
  }
}

/**
 * 设置分页
 * @param {Number} pageNum 页码
 * @param {Number} pageSize 每页条目数
 */
const setPage = (pageNum, pageSize) => {
  if (typeof pageNum === 'number') page.pageNum = pageNum
  if (typeof pageSize === 'number') page.pageSize = pageSize
}
/**
 * 设置查询条件
 */
const setCondition = (condition) => {
  conditionCache = condition instanceof Object ? JSON.parse(JSON.stringify(condition)) : undefined
}
/**
 * 获取当前表格被勾选的行数据
 */
const getSelectedRows = () => rowSelection.selectedRows

/**
 * 删除表格被勾选的行数据
 */
const deleteSelectedRows = () => {
  const ids = rowSelection.selectedRowKeys.join(',')
  if (!ids) {
    message.warning('未勾选数据')
    return
  }
  deletingRowIds = ids
  tipVisible.value = true
}
/** 获取分页信息 */
const getPage = () => page
watch(
  () => props.header,
  (v) => {
    if (v) {
      const cols = []
      const customColumns = []
      const normalAlign = ['left', 'right', 'center'].includes(props.align) ? props.align : 'left'
      v.forEach((item) => {
        let col = JSON.parse(JSON.stringify(item))
        col.key = col.dataIndex
        if (!col.align) col.align = normalAlign
        cols.push(col)
        if (item.slots) customColumns.push(item.slots.customRender)
      })
      if (props.buttonList && props.buttonList.list && props.buttonList.list.length > 0) {
        let { align, width, title, fixed } = props.buttonList
        cols.push({
          title: title || '操作',
          width: width || 160,
          fixed: fixed || false,
          dataIndex: '_operate',
          align: align || normalAlign,
          slots: { customRender: '_operate' }
        })
      }

      if (props.showIndex) cols.unshift({ title: '序号', dataIndex: 'index', width: 70, fixed: 'left', ...props.indexColumn })
      table.columns = cols
      table.customColumns = customColumns
    }
  },
  { immediate: true, deep: true }
)

onMounted(() => {
  setTimeout(() => {
    const cth = document.getElementById('customTable').offsetHeight
    const condition = document.getElementById('condition')
    const ch = condition ? condition.offsetHeight : 0
    const operate = document.getElementById('operate')
    const oh = operate ? operate.offsetHeight : 0
    const tableHeadHeight = 50
    const bottom = 40
    tableContentHeight.value = cth - ch - oh - tableHeadHeight - bottom
  }, 200)
  if (!props.sortDisabled) {
    initSortable()
  }
})

// 对外暴露的熟悉和方法
defineExpose({
  query,
  queryByCondition,
  resetQuery,
  setPage,
  getPage,
  deleteRow,
  setCondition,
  getSelectedRows,
  deleteSelectedRows
})

query()
</script>
